﻿using SharpDX.Direct3D11;
using System.Windows.Forms;
using SharpDX;
using SharpDX.D3DCompiler;
using SharpDX.DXGI;
using SharpDX.Windows;
using Device = SharpDX.Direct3D11.Device;
using Resource = SharpDX.Direct3D11.Resource;
using System.IO;
using ModelViewer;
using SharpDX.Direct3D;

namespace Engine
{
    public class EffectContainer : ResourceContainer, System.IDisposable
    {
        public InputLayout layout;
        public Effect renderEffect;
        public EffectTechnique[] renderTechniques;
        public int TechniqueCount;

        #region IDisposable
        //Ok
        ~EffectContainer()
        {
            Dispose();
        }

        public void Dispose()
        {
            if (layout != null) layout.Dispose();
            if (renderEffect != null) renderEffect.Dispose();
        }
        #endregion


        public EffectContainer(Device device, string EffectFile, InputElement[] _InputElement)
        {
            //Name = EffectFile;
            string extension = System.IO.Path.GetExtension(EffectFile);
            if (extension.ToLower() == ".fx")
            {
                IncludeFX includeFX = new IncludeFX();
                //Компилируем шейдер в байткод
#if !DEBUG
                //на время отладки шейдеров
                try
#endif
                {
                    SharpDX.Direct3D.ShaderMacro[] Macross = new SharpDX.Direct3D.ShaderMacro[6];
                    Macross[0] = new SharpDX.Direct3D.ShaderMacro("DX10_1", (Program.featureLevel > FeatureLevel.Level_10_0)? 1:0 );
                    Macross[1] = new SharpDX.Direct3D.ShaderMacro("SPLITCOUNT", PSSMsHelper.SplitsNumber);
                    Macross[2] = new SharpDX.Direct3D.ShaderMacro("OCCLPPIXELINTEX", DrawHelper.OcclPixelInTex);
                    Macross[3] = new SharpDX.Direct3D.ShaderMacro("SCREENX", Global.Settings.Width);
                    Macross[4] = new SharpDX.Direct3D.ShaderMacro("SCREENY", Global.Settings.Height);
                    

                    using (ShaderBytecode bytecode = ShaderBytecode.CompileFromFile(EffectFile, "fx_5_0",
#if DEBUG
                        ShaderFlags.Debug | ShaderFlags.SkipOptimization | ShaderFlags.PreferFlowControl,
#else
                        ShaderFlags.OptimizationLevel3,
#endif
                        EffectFlags.None, Macross, includeFX))
                    {
                        // Загрузим скомпилированный эффект в видеокарту
                        renderEffect = new Effect(device, bytecode);
                    }
                }
#if !DEBUG
                catch (System.Exception ex)
                {
                    System.Windows.Forms.MessageBox.Show("Error! Ошибкак чтения эффекта: " + EffectFile);
                    new CompilationException(EffectFile);
                }
#endif
            }
            else if (extension.ToLower() == ".ees")
            {
                try
                {
                    System.IO.FileStream fs = new System.IO.FileStream(EffectFile, System.IO.FileMode.Open, System.IO.FileAccess.Read);
                    byte[] bytes = new byte[(int)fs.Length];
                    fs.Read(bytes, 0, (int)fs.Length);
                    fs.Close();

                    using (DataStream DS = new DataStream(bytes.Length, true, true))
                    {
                        DS.Write(bytes, 0, bytes.Length);
                        using (ShaderBytecode bytecode = new ShaderBytecode(DS))
                        {
                            renderEffect = new Effect(device, bytecode);
                        }
                    }
                }
                catch (System.Exception ex)
                {
                    System.Windows.Forms.MessageBox.Show("Error! Ошибкак чтения эффекта: " + EffectFile);
                    new CompilationException(EffectFile);
                }
            }
            else
            {
                System.Windows.Forms.MessageBox.Show("Error! Не найден файл эффекта: " + EffectFile);
                new FileNotFoundException("Не найден файл", EffectFile);
            }

            //TODO изучить влияние
            renderEffect.Optimize();
            TechniqueCount = renderEffect.Description.TechniqueCount;
            renderTechniques = new EffectTechnique[TechniqueCount];
            // Загружаем техники из шейдера
            for (int i = 0; i < TechniqueCount; i++)
            {
                renderTechniques[i] = renderEffect.GetTechniqueByIndex(i);
            }
            // Выберем входную сигнатуру данных первого прохода
            var renderPassSignature = renderTechniques[0].GetPassByIndex(0).Description.Signature;

            if (_InputElement == null)
            {
                _InputElement= new InputElement[]{ 
                new InputElement("POSITION", 0, Format.R32G32B32_Float, 0, 0),
                new InputElement("NORMAL", 0, Format.R32G32B32_Float, 12, 0),
                new InputElement("TEXCOORD", 0, Format.R32G32_Float, 24, 0),
                new InputElement("TEXCOORD", 1, Format.R32G32_Float, 32, 0),
                new InputElement("TANGENT", 0, Format.R32G32B32_Float, 40, 0),
                new InputElement("BINORMAL", 0, Format.R32G32B32_Float, 52, 0)};

            }

            //создаем входную конфигурвцию для стандартного меша
            layout = new InputLayout(device, renderPassSignature, _InputElement);
        }
    }
    
    public class IncludeFX : Include
    {
        static string includeDirectory = "Content/Shaders/Include/";

        #region Члены Include
        public void Close(Stream stream)
        {
            stream.Close();
            stream.Dispose();
        }

        public Stream Open(IncludeType type, string fileName, Stream parentStream)
        {
            return new FileStream(includeDirectory + fileName, FileMode.Open);
        }
        #endregion

        #region Члены ICallbackable

        public System.IDisposable Shadow { get; set; }

        #endregion

        #region Члены IDisposable

        public void Dispose()
        {
            Shadow.Dispose();
        }
        #endregion
    }

}
